Skip to content

feat(scoring): surface the merged-PR history floor in the score breakdown - #1801

Merged
JSONbored merged 1 commit into
JSONbored:mainfrom
RenzoMXD:feat/scoring-merged-history-breakdown
Jun 30, 2026
Merged

feat(scoring): surface the merged-PR history floor in the score breakdown#1801
JSONbored merged 1 commit into
JSONbored:mainfrom
RenzoMXD:feat/scoring-merged-history-breakdown

Conversation

@RenzoMXD

Copy link
Copy Markdown
Contributor

Summary

explainScoreBreakdown (src/services/score-breakdown.ts) explains every other scoring multiplier -- density, contribution bonus, label, issue, credibility, review penalty, open-PR pressure, open-issue spam -- but silently omits the mergedHistoryMultiplier, even though it can zero the entire preview. The multiplier is already computed in src/scoring/preview.ts (a contributor's observed merged-PR count below the upstream floor MIN_VALID_MERGED_PRS zeroes it), feeds the final score, and has its own gate blocker (merged_pr_history_floor), yet had no entry in the components list.

What this adds

A mergedHistoryBreakdown component mirroring the existing openIssueBreakdown that #1453 added for the sibling open-issue gate:

  • neutral when no contributor history is observed (the floor is not enforced for the preview);
  • full when the observed merged-PR count meets the upstream floor;
  • blocked -- with a 100-leverage "land more merged PRs in this repo to clear the contributor-history floor" lever -- when the count is below the floor.

A contributor whose merged-PR history is below the upstream floor now sees an actionable lever instead of an opaque zero. Purely additive explanation; no scoring behavior change.

Scope

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage -- test/unit/score-breakdown.test.ts 11/11 pass; src/services/score-breakdown.ts is 100% statements / 100% functions / 100% lines (the new component's three branches -- unobserved, meets-floor, below-floor -- are each covered).

Targeted run:

npx vitest run test/unit/score-breakdown.test.ts --coverage --coverage.include='src/services/score-breakdown.ts'
# 11/11 passed; 100% statements/functions/lines

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed (a forbidden-term assertion is included).
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics (every summary/lever runs through the existing sanitizePublicComment).
  • No auth, cookie, CORS, GitHub App, Cloudflare, or session changes -- a purely additive explanation projection over an already-computed multiplier; no scoring logic change.
  • No UI changes.
  • No docs/changelog changes.

UI Evidence

Not applicable -- backend score-breakdown projection with no visible UI, frontend, docs, or extension surface.

Closes #1798

…down

explainScoreBreakdown explained every other scoring multiplier (density,
contribution bonus, label, issue, credibility, review penalty, open-PR
pressure, open-issue spam) but silently omitted the mergedHistoryMultiplier,
even though it can zero the entire preview when a contributor's observed
merged-PR count on the repo is below the upstream floor (MIN_VALID_MERGED_PRS).

Add a mergedHistoryBreakdown component mirroring the existing
openIssueBreakdown (PR JSONbored#1453): neutral when no contributor history is
observed (floor not enforced), full when the count meets the floor, and
blocked -- with a 100-leverage "land more merged PRs" lever -- when it is
below the floor. Purely additive explanation; no scoring behavior change.

Closes JSONbored#1798
@RenzoMXD
RenzoMXD requested a review from JSONbored as a code owner June 30, 2026 00:30
@dosubot dosubot Bot added the size:M label Jun 30, 2026
@loopover-orb

loopover-orb Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-06-30 06:44:06 UTC

2 files · 1 AI reviewer · no blockers · readiness 55/100 · CI green · unknown

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change adds the missing merged-history multiplier to the public breakdown and wires it into the existing sanitizer/highest-leverage flow. The three reachable states are covered in the unit test, and the implementation follows the existing open-PR/open-issue component pattern without changing scoring behavior. I do not see a diff-scoped correctness defect.

Nits — 7 non-blocking
  • nit: src/services/score-breakdown.ts:104 adds a three-line explanatory comment that mostly restates the function body; trimming it would keep this file closer to the existing concise helper style.
  • nit: test/unit/score-breakdown.test.ts:99 covers the bands but does not assert the new summary text includes the observed count/floor, which is the user-visible value this PR is adding.
  • In test/unit/score-breakdown.test.ts:115, assert the blocked component summary or lever contains both the observed merged count and configured floor so regressions in the new explanation text are caught.
  • In src/services/score-breakdown.ts:122, consider storing `const meetsFloor = mergedPullRequests >= mergedPrFloor;` and reusing it for summary, lever, and leverageScore to avoid repeating the same branch condition.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #1798
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:M; 1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR; address the blocker before review.
Contributor workload ✅ 10/10 Author activity: 49 registered-repo PR(s), 23 merged, 8 issue(s).
Contributor context ✅ Confirmed Gittensor contributor RenzoMXD; Gittensor profile; 49 PR(s), 8 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
Contributor next steps
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Fix the blocker.
  • Triage stale or unlinked PRs.
  • Refresh registry data or choose a registered active repo.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jun 30, 2026
@dosubot dosubot Bot added the lgtm label Jun 30, 2026
@JSONbored
JSONbored merged commit e437898 into JSONbored:main Jun 30, 2026
15 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jun 30, 2026
@RenzoMXD
RenzoMXD deleted the feat/scoring-merged-history-breakdown branch June 30, 2026 11:15
JSONbored pushed a commit that referenced this pull request Jun 30, 2026
…wn (#1877)

explainScoreBreakdown explained every other scoring multiplier (density,
contribution bonus, label, issue, credibility, review penalty, open-PR
pressure, open-issue spam, merged-PR history floor in #1801, and the
issue-discovery validity floor now in #1874) but silently omitted the
timeDecayMultiplier, even though it multiplies into the final score
(preview.ts:432) and is the only remaining multiplier on scoreEstimate
without a breakdown component.

Add a timeDecayBreakdown: neutral when the multiplier is >= 0.99 (the
PR is fresh or upstream time-decay -- env SCORING_TIME_DECAY_ENABLED,
default OFF -- is disabled for the preview), and reduced with an aged-PR
lever when the multiplier has decayed. Purely additive explanation; no
scoring behavior change.
JSONbored pushed a commit that referenced this pull request Jul 1, 2026
…eakdown (#2356)

* feat(scoring): surface the saturated base-score value in the score breakdown

explainScoreBreakdown explained every multiplier on scoreEstimate that
scales the score (density, contribution bonus, label, issue, credibility,
review-penalty, review-collateral, open-PR pressure, open-issue spam, the
merged-PR history floor from #1801, the issue-discovery validity floor from
#1984, and the time-decay floor from #1877) but silently omitted the
**baseScore** itself — the saturated-value foundation
`25 × (1 - exp(-src_tok / SRC_TOK_SATURATION_SCALE=58))
 + min(total_token_score / CONTRIBUTION_SCORE_FOR_FULL_BONUS=1500, 1) × MAX_CONTRIBUTION_BONUS=5`
(capped at 30) that flows into estimatedMergedScore before any multiplier
applies. A contributor could see their `densityMultiplier` was healthy but
had no surface for the actual cap contribution or the contribution-bonus
adder.

Add a `baseScoreBreakdown` sibling of `densityBreakdown` (which already
uses `baseTokenGatePassed` as a gate trigger) that:

- returns `blocked` when `baseTokenGatePassed` is false (the change does
  not yet meet the minimum meaningful source-change threshold),
- returns `full` when baseScore is saturated near the 30-point cap and
  names whether the contribution bonus is contributing,
- returns `neutral` when baseScore is mid-curve, with a copy that names
  BOTH the baseScore and the contributionBonus dimensions explicitly
  (avoids the §7 Tier A rule 3 collapse-copy nit — the production copy
  always iterates on observed values for both axes, no "no source
  contribution observed" single-clause ever produced).

Purely additive explanation projection; no scoring behavior change.

Wiring:

- src/services/score-breakdown.ts: add `baseScoreBreakdown`, insert
  at index 0 of the components projection (foundation before density /
  contributionBonus), under the `leverageScore` convention that keeps
  `densityMultiplier` as the canonical top lever when both are blocked
  (baseScore blocked = 70, below densityMultiplier's 75).
- test/unit/score-breakdown.test.ts: include the new `baseScore`
  component in the top-level `arrayContaining` regression list, and add
  a focused test covering the blocked / neutral-with-bonus / saturated
  branches (avoids the §7 Tier A rule 5 missing-both-branches test gap).

Verified locally:

- `git diff --check` clean.
- `npm run typecheck` clean.
- `npm run actionlint` clean.
- `npm run db:migrations:check` — 90 migrations OK, contiguous 0001..0087.
- `npm run build:mcp` clean.
- `npx vitest run test/unit/score-breakdown.test.ts --coverage
  --coverage.include='src/services/score-breakdown.ts'` —
  **15/15 tests pass**; 100% statements; 100% lines; 3 uncovered
  branches in pre-existing code (issueMultiplierBreakdown:240 +
  2 in pre-existing reviewCollateralBreakdown), not touched by this change.

Diff stat: `src/services/score-breakdown.ts` +34 lines,
`test/unit/score-breakdown.test.ts` +60 lines, total +94 lines
(`size:S` territory; orb counts src files primarily per §7 Tier A rule 1).

* fix(scoring): address review nits on base-score breakdown

- Fix test fixture MAX_CONTRIBUTION_BONUS from 25 to 5 (match production)
- Replace hardcoded '30-point cap' with generic 'score cap' text
- Extract BASE_SCORE_SATURATION_DISPLAY_THRESHOLD (29.5) as named constant
- Bump baseScore blocked leverageScore from 70 to 75 (match density gate block)
- Split 3-branch test into separate focused it() blocks
- Add test for hasBonus=false + gate-passed branch

* fix(scoring): derive base-score saturation from preview-estimate cap, not hardcoded threshold

Hardcoding BASE_SCORE_SATURATION_DISPLAY_THRESHOLD=29.5 meant any preview
using different MERGED_PR_BASE_SCORE or MAX_CONTRIBUTION_BONUS constants
would mislabel saturated vs sub-cap.

- Add baseScoreCap to ScorePreviewResult.scoreEstimate (computed alongside
  baseScore in the scoring core, which has the snapshot constants)
- Replace hardcoded 29.5 with BASE_SCORE_SATURATION_RATIO=0.95 applied to
  baseScore / baseScoreCap — always relative to the active model's cap
- baseScoreCap is undefined when fixedBaseScore is in effect
- Add regression test with MERGED_PR_BASE_SCORE=50, MAX_CONTRIBUTION_BONUS=10
  proving the threshold adapts to non-default constants

* fix: guard baseScoreCap > 0 before division, fix test comment

- Add baseScoreCap > 0 guard before saturation ratio division
- Fix test comment: 993 is the 95% threshold value, not 'full bonus'

* fix: remove dead locals from test, handle baseScoreCap undefined copy

* test: cover fixedBaseScore and saturation model cap branches

- fixedBaseScore override → baseScoreCap undefined, copy says 'fixed base score override'
- pending_saturation_model → cap = MERGED_PR_BASE_SCORE + MAX_CONTRIBUTION_BONUS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

feat(scoring): surface the merged-PR history floor in the score breakdown

2 participants